package com.knowswift.security.account;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.knowswift.common.bean.wechat.dto.WechatResponse;
import com.knowswift.common.enums.RoleEnum;
import com.knowswift.common.utils.WrappedBeanCopier;
import com.knowswift.security.AuthUserDetails;
import com.knowswift.security.AuthUserDetailsService;
import com.knowswift.security.exception.AuthException;
import com.knowswift.security.mapper.UserMapper;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;


@Service
public class UserService extends ServiceImpl<UserMapper, User> implements AuthUserDetailsService {

    @Override
    public AuthUserDetails loadUserByUsername(String s) throws AuthException {
        User user = getOne(new LambdaQueryWrapper<User>().eq(User::getUserAccount, s));
        if (user == null) {
            throw new AuthException(300, "账号不存在");
        }
        return user;
    }

    @Override
    public AuthUserDetails loadUserByAccount(String account) throws AuthException {
        return loadUserByUsername(account);
    }

    @Override
    public AuthUserDetails loadUserByPhoneNumber(String phoneNumber) throws UsernameNotFoundException {
        User user = getOne(new LambdaQueryWrapper<User>().eq(User::getUserPhone, phoneNumber));
        if (user == null) {
            return createUser(null, null, phoneNumber);
        }
        return user;
    }

    @Override
    public AuthUserDetails loadUserByMiniOpenId(String unionId, String openId, WechatResponse decrypt) throws UsernameNotFoundException {
        User user = getOne(new LambdaQueryWrapper<User>().eq(User::getMiniOpenId, openId));
        // 如此微信未登录过
        if (user == null) {
            String phone = null;
            if (decrypt != null) {
                phone = decrypt.getPurePhoneNumber();
                user = getOne(new LambdaQueryWrapper<User>().eq(User::getUserPhone, phone));
                if (user != null) {
                    user.setMiniOpenId(openId);
                    user.setWechatUnionId(unionId);
                    return user;
                }
            }
            user = createUser(unionId, openId, phone);
        }
        // 如果原先登录的手机号码和当前的不一致，如：10086 现在是10000
        if (decrypt != null && user.getUserId() != null && !user.getUserPhone().equals(decrypt.getPurePhoneNumber())) {
            // 原来的手机账号 清除微信相关信息
            user.setMiniOpenId(null);
            user.setWechatUnionId(null);
            user.setOfficialOpenId(null);
            user.setSubscribed(false);
            user.setAdminTokenVersion(user.getAdminTokenVersion() == 0 ? 1 : 0);
            user.setAppTokenVersion(user.getAppTokenVersion() == 0 ? 1 : 0);
            updateById(user);
            // 根据当前手机号码查询
            user = getOne(new LambdaQueryWrapper<User>().eq(User::getUserPhone, decrypt.getPurePhoneNumber()));
            if (user == null) {
                user = createUser(unionId, openId, decrypt.getPurePhoneNumber());
            }
        }
        user.setMiniOpenId(openId);
        user.setWechatUnionId(unionId);
        return user;
    }

    @Override
    public AuthUserDetails loadUserByOfficialOpenId(String unionId, String openId) {
        User user = getOne(new LambdaQueryWrapper<User>().eq(User::getOfficialOpenId, openId));
        if (user == null) {
            user = createUser(unionId, openId, "");
        }
        return user;
    }

    @Override
    public AuthUserDetails loadUserByUnionId(String unionId, String openId) {
        return getOne(new LambdaQueryWrapper<User>().eq(User::getWechatUnionId, unionId));
    }

    public AuthUserDetails loadUserById(String id) {
        return getById(id);
    }

    @Override
    public AuthUserDetails loadUserByAppleId(String id) {
        return lambdaQuery().eq(User::getAppleId, id).oneOpt().orElse(createUser(id));
    }

    private User createUser(String unionId, String openId, String phone) {
        User user = new User();
        user.setRole(RoleEnum.ROLE_USER.name());
        user.setLocked(false);
        user.setAppTokenVersion(0);
        user.setAdminTokenVersion(0);
        user.setWechatUnionId(unionId);
        user.setMiniOpenId(openId);
        user.setUserPhone(phone);
        return user;
    }

    private User createUser(String appleId) {
        User user = new User();
        user.setRole(RoleEnum.ROLE_USER.name());
        user.setLocked(false);
        user.setAppTokenVersion(0);
        user.setAppleId(appleId);
        return user;
    }


    public User getByPhone(String phoneNumber) {
        return getOne(new LambdaQueryWrapper<User>().eq(User::getUserPhone, phoneNumber));
    }

    public List<Authority> allAuthorities() {
        List<Authority> list = new ArrayList<>();
        Authority account = new Authority("账号管理", "accountMg");
        account.add("查看账号", "accounts").add("添加、修改、冻结、解冻、删除账号", "editAccount").add("白蚁监测设置", "monitorSetting");

        Authority project = new Authority("项目设置", "projectMg");
        project.add("查看项目设置", "projects").add("添加、修改、删除项目模块和名称", "editProject");

        Authority monitor = new Authority("有害生物监测", "monitorManagement");
        Authority device = new Authority("白蚁智能诱杀监测装置列表", "monitorDevices").add("实时电量","electricMonitor").add("装置异常","errorMonitor").add("编辑装置","editMonitor");
        monitor.add(device);
//        monitor.add("白蚁智能诱杀监测装置列表","monitorDevices").add("白蚁监测地图","monitorMap").add("白蚁监测统计","monitorStatistics");

        Authority issues = new Authority("查看项目数据", "issueView");
        issues.add("查看全部上报数据", "allIssues")
                .add("仅能查看本人提交的上报数据", "ownIssues", false)
                .add(new Authority("根据项目查看", "projectIssues", false));

        Authority data = new Authority("项目数据管理", "issueMg");
        data.add("审核上报数据", "auditIssue").add("修改、删除上报数据", "editIssue");

// 添加客户管理权限
        Authority customerManage = new Authority("客户管理", "customerManage");
        customerManage.add("查看", "customerManage-view")
                .add("新增", "customerManage-add")
                .add("编辑", "customerManage-edit")
                .add("启用/禁用", "customerManage-active")
                .add("授权", "customerManage-auth")
                .add("删除", "customerManage-del");

// 添加客户账号权限
        Authority customerAccount = new Authority("客户账号", "customerAccount");
        customerAccount.add("查看", "customerAccount-view")
                .add("新增", "customerAccount-add")
                .add("编辑", "customerAccount-edit")
                .add("启用/禁用", "customerAccount-active")
                .add("授权", "customerAccount-auth")
                .add("删除", "customerAccount-del");
        list.add(data);
        list.add(issues);
        list.add(monitor);
        list.add(project);
        list.add(account);
        list.add(customerManage);
        list.add(customerAccount);
        return list;
    }

    public List<Authority> emptyAuthorities() {
        List<Authority> authorities = allAuthorities();
        changeLimit(authorities, false);
        return authorities;
    }

    private void changeLimit(List<Authority> authorities, boolean limit) {
        if (CollectionUtils.isEmpty(authorities)) {
            return;
        }
        for (Authority authority : authorities) {
            authority.setLimit(limit);
            changeLimit(authority.getItems(), limit);
        }
    }

    public boolean checkAuthority(String... values) {
        SecurityContext context = SecurityContextHolder.getContext();
        User user = (User) context.getAuthentication().getPrincipal();
        if ((RoleEnum.isAdmin(user.getRole()))) {
            return true;
        }
        List<Authority> authorities = WrappedBeanCopier.JSONArrayToList(user.getUserAuthorities(), Authority.class);

        for (String s : values) {
            for (Authority authority : authorities) {
                if (authority.getValue().equals(s)) {
                    return BooleanUtils.isTrue(authority.getLimit());
                }
                if (checkAuthority(authority.getItems(), s)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean checkAuthority(List<Authority> items, String value) {
        for (Authority item : items) {
            if (item.getValue().equals(value)) {
                return BooleanUtils.isTrue(item.getLimit());
            }
            checkAuthority(item.getItems(), value);
        }
        return false;
    }

    private static boolean checkItems(List<Authority> items, String value) {
        for (Authority item : items) {
            if (item.getValue().equals(value)) {
                return BooleanUtils.isTrue(item.getLimit());
            }
            checkItems(item.getItems(), value);
        }
        return false;
    }

    public static boolean checkAuthority(User user, String... values) {
        if ((RoleEnum.isAdmin(user.getRole()))) {
            return true;
        }
        List<Authority> authorities = WrappedBeanCopier.JSONArrayToList(user.getUserAuthorities(), Authority.class);
        for (String s : values) {
            for (Authority authority : authorities) {
                if (authority.getValue().equals(s)) {
                    return BooleanUtils.isTrue(authority.getLimit());
                }
                if (checkItems(authority.getItems(), s)) {
                    return true;
                }
            }
        }
        return false;
    }
}
